!pip install pandas-profiling==2.7.1
import pandas as pd
import numpy as np
import scipy.stats as stats
from scipy.stats import chi2
from scipy.stats import chi2_contingency
import requests
import zipfile
import seaborn as sns
import matplotlib.pyplot as plt
plt.style.use("ggplot")
import pandas_profiling
def download_content(url):
r = requests.get(url)
with open('bank-additional.zip', "wb") as f:
f.write(r.content)
return
download_content('https://archive.ics.uci.edu/ml/machine-learning-databases/00222/bank-additional.zip')
with zipfile.ZipFile('bank-additional.zip', 'r') as zip_ref:
zip_ref.extractall('zip')
df = pd.read_csv('zip/bank-additional/bank-additional-full.csv',delimiter=";")
df.head()
pandas_profiling.ProfileReport(df)
Los datos están relacionados con campañas de marketing de una entidad financiera portuguesa. Las campañas de marketing se basaron en llamadas a teléfono y celular. El dataframe con tiene 41188 observaciones y 21 variables, de estas 10 son categóricas, 10 numéricas y 1 tipo boolean o binaria. Las variables contienen información demográfica del cliente, información relacionada con el último contacto de la última campaña, información del contexto social y económico e información del cliente con respecto a otras campañas.
En este link se puede encontrar información más detallada respecto a las variables
df.dtypes
df[df.duplicated()==True]
def completitud(df):
mis_val = df.isnull().sum()
mis_val_percent = 100 * (1 - (df.isnull().sum()/len(df)))
mis_val_percent.rename_axis('Atributo',inplace=True)
return mis_val_percent
completitud(df)
Podemos ver que las variables job, marital, education, default, housing y loan contienen valores "unknown" por lo que es mejor eliminar las filas que contengan estos valores siempre y cuando tengan pocas filas que los incluyan.
for columna in df.columns:
print(columna,"\n",df[columna].unique())
Veremos la cantidad de filas que tienen unknown en los valores de las columnas job, marital, education, housing, loan y default para determinar si se pueden eliminar o no algunas filas.
cols = ['job','marital','education','housing','loan','default']
for col in cols:
print(pd.value_counts(df[col]))
cols = ['job','marital','education','housing','loan']
for col in cols:
df = df[df[col]!='unknown']
print(f"Quedan {df.shape[0]} filas y {df.shape[1]} columnas")
df.head()
df.drop(columns=['pdays','default','month','duration'], inplace=True)
df.head()
Las hipótesis a plantear están relacionadas con una caracterización de la población de manera que sea más fácil identificar la población objetivo para las campañas futuras basados en la aceptación de campañas previas.
Hipótesis 1: El nivel educativo está relacionado con la aceptación de estas campañas por parte de los clientes, entre más alto mayor aceptación puede haber.
Hipótesis 2: El estado civil está relacionado con la aceptación de estas campañas por parte de los clientes, entre más comprometido mayor aceptación puede haber.
Hipótesis 3: El tener préstamo de vivienda está relacionado negativamente con la aceptación de estas campañas por parte de los clientes.
Hipótesis 4: El tener préstamo a nivel personal está relacionado negativamente con la aceptación de estas campañas por parte de los clientes.
def freq_total(col,df):
Tabla = pd.crosstab(index=df[col],columns=df["y"],margins=True)
cols = list(Tabla.columns)
cols[-1] = "Total_"+col
Tabla.columns = cols
idxs = list(Tabla.index)
idxs[-1] = "Total_y"
Tabla.index = idxs
return Tabla/Tabla.loc['Total_y','Total_'+col]
def freq_relativCol(col,df):
Tabla = pd.crosstab(index=df[col],columns=df["y"],margins=True)
cols = list(Tabla.columns)
cols[-1] = "Total_"+col
Tabla.columns = cols
idxs = list(Tabla.index)
idxs[-1] = "Total_y"
Tabla.index = idxs
return Tabla.div(Tabla.loc['Total_y',:],axis=1)
def freq_relativFil(col,df):
Tabla = pd.crosstab(index=df[col],columns=df["y"],margins=True)
cols = list(Tabla.columns)
cols[-1] = "Total_"+col
Tabla.columns = cols
idxs = list(Tabla.index)
idxs[-1] = "Total_y"
Tabla.index = idxs
return Tabla.div(Tabla["Total_"+col], axis=0)
freq_total('education',df)
freq_relativCol('education',df)
Tabla = freq_relativFil('education',df)
Tabla
ax = (Tabla[["no", "yes"]]).plot(kind='bar',figsize=(15,4),width = 0.8,edgecolor=None)
plt.legend(labels=Tabla.columns,fontsize= 14)
plt.title("Porcentaje de aceptación por nivel educativo",fontsize= 16)
plt.xticks(fontsize=14, rotation=45)
for spine in plt.gca().spines.values():
spine.set_visible(False)
plt.yticks([])
# Add this loop to add the annotations
for p in ax.patches:
width, height = p.get_width(), p.get_height()
x, y = p.get_xy()
ax.annotate('{:.0%}'.format(height), (x, y + height + 0.01))
Parece que la población perteneciente a un nivel educativo superior o igual a high_school son los más propensos a aceptar respecto a los que no. Sin embargo, los iletrados son los miembros del nivel educativo que más aceptan.
freq_total('marital',df)
freq_relativCol('marital',df)
Tabla = freq_relativFil('marital',df)
Tabla
ax = (Tabla[["no", "yes"]]).plot(kind='bar',figsize=(15,4),width = 0.8,edgecolor=None)
plt.legend(labels=Tabla.columns,fontsize= 14)
plt.title("Porcentaje de aceptación por estado civil",fontsize= 16)
plt.xticks(fontsize=14, rotation=0)
for spine in plt.gca().spines.values():
spine.set_visible(False)
plt.yticks([])
# Add this loop to add the annotations
for p in ax.patches:
width, height = p.get_width(), p.get_height()
x, y = p.get_xy()
ax.annotate('{:.0%}'.format(height), (x, y + height + 0.01))
Parece que las personas casadas o divorciadas aceptan en igual medida pero menos con respecto a los solteros.
freq_total('housing',df)
freq_relativCol('housing',df)
Tabla = freq_relativFil('housing',df)
Tabla
ax = (Tabla[["no", "yes"]]).plot(kind='bar',figsize=(15,4),width = 0.8,edgecolor=None)
plt.legend(labels=Tabla.columns,fontsize= 14)
plt.title("Porcentaje de aceptación por préstamo de vivienda",fontsize= 16)
plt.xticks(fontsize=14, rotation=0)
for spine in plt.gca().spines.values():
spine.set_visible(False)
plt.yticks([])
# Add this loop to add the annotations
for p in ax.patches:
width, height = p.get_width(), p.get_height()
x, y = p.get_xy()
ax.annotate('{:.0%}'.format(height), (x, y + height + 0.01))
Parece que el tener un préstamo de vivienda o no no afecta la aceptación
freq_total('loan',df)
freq_relativCol('loan',df)
Tabla = freq_relativFil('loan',df)
Tabla
ax = (Tabla[["no", "yes"]]).plot(kind='bar',figsize=(15,4),width = 0.8,edgecolor=None)
plt.legend(labels=Tabla.columns,fontsize= 14)
plt.title("Porcentaje de aceptación por préstamo personal",fontsize= 16)
plt.xticks(fontsize=14, rotation=0)
for spine in plt.gca().spines.values():
spine.set_visible(False)
plt.yticks([])
# Add this loop to add the annotations
for p in ax.patches:
width, height = p.get_width(), p.get_height()
x, y = p.get_xy()
ax.annotate('{:.0%}'.format(height), (x, y + height + 0.01))
Parece que el tener un préstamo personal no afecta la aceptación.
Las hipótesis a validar seleccionadas son las dos primeras
Hipótesis 1: El nivel educativo está relacionado con la aceptación de estas campañas por parte de los clientes, entre más alto mayor aceptación puede haber.
Hipótesis 2: El estado civil está relacionado con la aceptación de estas campañas por parte de los clientes, entre más comprometido mayor aceptación puede haber.
def contingencia(col,df):
Tabla = pd.crosstab(index=df[col],columns=df["y"])
return Tabla
def chiquad(Tabla, prob = 0.95):
estadistico, p_value, grados_libertad, freq_esperadas = chi2_contingency(Tabla)
print(f"El estadístico de prueba es {estadistico}, el p_value es {p_value}, los grados de libertad {grados_libertad} y las frecuencias esperadas son:\n {freq_esperadas}")
print("Interpretando p_value con probabilidad del {}%".format(prob*100))
alpha = 1.0 - prob
if p_value <= alpha:
print('Dependientes (Se rechaza H0)')
else:
print('No son dependientes (No se puede rechazar H0)')
print("Interpretando estadístico de prueba con probabilidad del {0}% y {1} grados de libertad".format(prob*100,grados_libertad))
# interpret test-statistic
critical = chi2.ppf(prob, grados_libertad)
if abs(estadistico) >= critical:
print('Dependientes (Se rechaza H0)')
else:
print('No son dependientes (No se puede rechazar H0)')
H0: Las variables education y y son independientes
H1: Las variables education y y no son independientes
Tabla = contingencia("education",df)
chiquad(Tabla)
Las variables education y y no son independientes
H0: Las variables marital y y son independientes
H1: Las variables marital y y no son independientes
Tabla = contingencia("marital",df)
chiquad(Tabla)
Las variables marital y y no son independientes
La estrategia propuesta dada la relación de las variables education y marital con la variable y (aceptación de la suscripción al depósito) debe ser una campaña dirigida a personas iletradas o con nivel educativo superior o igual a high school y que sean solteras aunque también pueden ser personas casadas y divorciadas pues la proporción de personas que aceptaron previamente no varia mucho entre los estados civiles anteriormente mencionados.